﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Taxonomy;


namespace SPHelper
{
	public class Item
	{
		/// <summary>
		/// Create a new list item. Saving to database is optional
		/// </summary>
		/// <param name="list">List to add item to</param>
		/// <param name="title"></param>
		/// <param name="isSave">is save to database</param>
		/// <returns></returns>
		public static SPListItem CreateItem(SPList list, string title, bool isSave)
		{
			SPListItem newItem = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }

				newItem = list.Items.Add();
				newItem["Title"] = title;

				// save to db
				if (isSave) { newItem.Update(); }
			}
			catch (Exception ex)
			{
				Log.WriteError("dyjfuylfgfgfgddrrd", ex, "item title: " + title);
				newItem = null;
			}
			return newItem;
		}

		public enum TaskPriority
		{
			High = 1,
			Normal,
			Low,
		}
		public enum TaskStatus
		{
			Not_Started,
			In_Progress,
			Completed,
			Deferred,
			Waiting_on_someone_else,
		}

		public static void CreateTaskItem(SPList taskList, string title, SPUser assignedTo, TaskPriority priority,
			TaskStatus status, float? percentComplete, DateTime? startDate, DateTime? dueDate, int? predecessors)
		{
			try
			{
				// init
				if (taskList == null) { throw new Exception("SPList passed is null"); }
				if (string.IsNullOrEmpty(title)) { throw new Exception("Title passed is null"); }
				if (percentComplete == null) { percentComplete = 0; }
				if (startDate == null || startDate == DateTime.MinValue) { startDate = DateTime.Now; }
				if (dueDate == null || dueDate == DateTime.MinValue) { dueDate = DateTime.Now; }


				// assigned to
				if (assignedTo == null)
				{
					if (SPContext.Current != null && SPContext.Current.Web != null)
					{
						assignedTo = SPContext.Current.Web.CurrentUser;
					}
					else
					{
						throw new Exception("Assigned to user passed is null and failed to get current SPUser");
					}
				}

				SPListItem newItem = CreateItem(taskList, title, false);
				newItem["AssignedTo"] = assignedTo;
				newItem["Priority"] = priority.ToString();
				newItem["Status"] = status.ToString().Replace("_", " ");
				newItem["PercentComplete"] = (float)percentComplete;

				if (predecessors != null && predecessors > 0)
				{
					newItem["Predecessors"] = (int)predecessors;
				}

				newItem["StartDate"] = startDate;
				newItem["DueDate"] = dueDate;

				// save
				newItem.Update();
			}
			catch (Exception ex)
			{
				Log.WriteError("7t98pyflukdfguydrtu", ex);
			}
		}


		/// <summary>
		/// Get SPListItem by absolute url passed, only works for a document library!!!
		/// http://blog.mastykarz.nl/inconvenient-spweb-getlistitem-exception-hresult-0x80070001/
		/// </summary>
		/// <param name="web"></param>
		/// <param name="url">server relative url</param>
		/// <returns></returns>
		public static SPListItem GetItemByUrl(SPWeb web, string url)
		{
			SPListItem item = null;
			try
			{
				// init
				if (web == null) { web = Web.GetWeb(); }
				if (web == null) { throw new Exception("Failed to get SPWeb from SPContext"); }
				if (string.IsNullOrEmpty(url)) { throw new Exception("Url passed is null"); }

				item = web.GetListItem(url);
			}
			catch (Exception ex)
			{
				Log.WriteError("zdsghfyttdydfxfd", ex, "url: " + url);
				item = null;
			}
			return item;
		}

		/// <summary>
		/// Get item by itemId int
		/// </summary>
		/// <param name="list"></param>
		/// <param name="itemId"></param>
		/// <returns></returns>
		public static SPListItem GetItemById(SPList list, int itemId)
		{
			SPListItem item = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }

				item = list.GetItemById(itemId);
			}
			catch (Exception ex)
			{
				Log.WriteError("etwew5rsfdaserf", ex, "itemId: " + itemId);
				item = null;
			}
			return item;
		}

		/// <summary>
		/// get list item by GUID id
		/// </summary>
		/// <param name="list"></param>
		/// <param name="uniqueId"></param>
		/// <returns></returns>
		public static SPListItem GetItemByUniqueId(SPList list, Guid uniqueId)
		{
			SPListItem item = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }
				if (uniqueId == null || uniqueId == Guid.Empty) { throw new Exception("UniqueId passed is null"); }

				list.GetItemByUniqueId(uniqueId);
			}
			catch (Exception ex)
			{
				Log.WriteError("sreyt5yewy5u5weews", ex);
				item = null;
			}
			return item;
		}

		/// <summary>
		/// Get a list by it's title. Will enumerate every single SPListItem in the list to find it
		/// WARNING: This can be slow if the list is very large!
		/// </summary>
		/// <param name="list"></param>
		/// <param name="title"></param>
		/// <returns></returns>
		public static SPListItem GetItemByTitle(SPList list, string title)
		{
			SPListItem item = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }
				if (string.IsNullOrEmpty(title)) { throw new Exception("SPListItem title passed is null"); }


				// get all items with matching title
				SPListItemCollection items = GetItems(list, "Text", "Title", title);
				if (items.Count != 0)
				{
					item = items[0];
					if (items.Count > 1)
					{
						string duplicateItemsUrls = null;
						foreach (SPListItem i in items) { duplicateItemsUrls += GetUrl(i, true, false) + "\r\n"; }
						Log.WriteError("dsgfgdsdrsasdg", "Returned " + items.Count + " items for list: "
							+ list.Title + " using title: " + title + "\r\n\r\nItems with Same Title:\r\n" 
							+ duplicateItemsUrls);
					}
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("ytfyggdfjf", ex, "title: " + title);
				item = null;
			}
			return item;
		}

		/// <summary>
		/// Get Item by Field Value
		/// </summary>
		/// <param name="list"></param>
		/// <param name="fieldType">If null, default is "Text"</param>
		/// <param name="fieldName"></param>
		/// <param name="fieldValue"></param>
		/// <returns></returns>
		public static SPListItem GetItemByFieldValue(SPList list, string fieldType, string fieldName, string fieldValue)
		{
			SPListItem item = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }

				// get all items with matching title
				SPListItemCollection items = GetItems(list, fieldType, fieldName, fieldValue);
				if (items.Count != 0)
				{
					item = items[0];
					if (items.Count > 0)
					{
						Log.WriteError("srtudtsrdsdrtr", "Returned more than one item for list: "
							+ list.Title + " using field: " + fieldName + " with value: " + fieldValue);
					}
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("futdfjhdddtyyt", ex, "type: " + fieldType + " name: " + fieldName + " value: " + fieldValue);
				item = null;
			}
			return item;
		}




		/// <summary>
		/// Gets SPListItems, if query is null, returns all
		/// </summary>
		/// <param name="list"></param>
		/// <param name="query">If null, returns all where title is not null</param>
		/// <returns></returns>
		public static SPListItemCollection GetItems(SPList list, SPQuery query)
		{
			string queryText = null;
			SPListItemCollection items = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }
				if (query == null)
				{
					// query to select all
					query = new SPQuery();
				}
				else
				{
					queryText = query.Query;
				}

				items = list.GetItems(query);
			}
			catch (Exception ex)
			{
				Log.WriteError("ertyfdfdsr", ex, "query: " + queryText);
				items = null;
			}
			return items;
		}

		/// <summary>
		/// Get a collection of items by matching a field to a specific value 
		/// </summary>
		/// <param name="list"></param>
		/// <param name="fieldType">If null, default is "Text"</param>
		/// <param name="fieldName"></param>
		/// <param name="fieldValue"></param>
		/// <returns></returns>
		public static SPListItemCollection GetItems(SPList list, string fieldType, string fieldName, string fieldValue)
		{
			SPListItemCollection items = null;
			string queryText = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }
				if (string.IsNullOrEmpty(fieldName)) { throw new Exception("Field name is null"); }
				if (string.IsNullOrEmpty(fieldType)) { fieldType = "Text"; }


				// build query
				SPQuery q = new SPQuery();
				q.Query = "<Where><Eq><FieldRef Name='" + fieldName + "' /><Value Type='" + fieldType + "'>"
					+ fieldValue + "</Value></Eq></Where>";
				queryText = q.Query;

				items = list.GetItems(q);
			}
			catch (Exception ex)
			{
				Log.WriteError("drgysreutdr7dsdrrdsz", ex, "CAML: " + queryText);
				items = null;
			}
			return items;
		}

		/// <summary>
		/// Get many items by passing a collection of values to search against
		/// http://stackoverflow.com/questions/1891303/sql-in-equivalent-in-caml
		/// </summary>
		/// <param name="list"></param>
		/// <param name="fieldType"></param>
		/// <param name="fieldName"></param>
		/// <param name="fieldValues"></param>
		/// <param name="fieldsToReturn">The fields to return for SPListItems in returned collection. Use this if SPListItems contain lots of complex fields like managed metadata</param>
		/// <returns></returns>
		public static SPListItemCollection GetItems(SPList list, string fieldType, string fieldName, object[] fieldValues,
			string[] fieldsToReturn, int pageNumber, int pageSize, ref int totalItems)
		{
			SPListItemCollection items = null;
			string query = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }
				if (string.IsNullOrEmpty(fieldName)) { throw new Exception("Field name is null"); }
				if (string.IsNullOrEmpty(fieldType)) { fieldType = "Text"; }
				if (fieldName == "ID") { fieldType = "Counter"; }
				if (fieldValues == null) { throw new Exception("Fieldvalues passed is null"); }
				if (fieldValues.Length > 500) { throw new Exception("Microsoft SharePoint Limits to 500 items in CAML predicate"); }

				if (fieldValues.Length > 0)
				{
					// build query
					query = "<Where><In><FieldRef Name='" + fieldName + "' /><Values>";
					foreach (var v in fieldValues) { query += "<Value Type='" + fieldType + "'>" + v.ToString() + "</Value>"; }
 					query += "</Values></In></Where>";

					// call original
					items = GetItems(list, query, fieldsToReturn, pageNumber, pageSize, ref totalItems);
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("tyuyjfhtddgdrte5tr", ex, "CAML: " + query);
				items = null;
			}
			return items;
		}

		/// <summary>
		/// Get many items by passing a collection of values to search against
		/// </summary>
		/// <param name="list"></param>
		/// <param name="fieldType"></param>
		/// <param name="fieldName"></param>
		/// <param name="fieldValues"></param>
		/// <returns></returns>
		public static SPListItemCollection GetItems(SPList list, string fieldType, string fieldName, object[] fieldValues)
		{
			// call related member
			int _totalItems = 0;
			return GetItems(list, fieldType, fieldName, fieldValues, null, 0, 0, ref _totalItems);
		}


		/// <summary>
		/// Get items by itemIds
		/// </summary>
		/// <param name="list"></param>
		/// <param name="itemIds"></param>
		/// <param name="orderByFields">field name, sort order list of fields for ordering</param>
		/// <returns></returns>
		public static SPListItemCollection GetItems(SPList list, int[] itemIds, List<Caml.OrderByField> orderByFields,
			string[] fieldsToReturn)
		{
			SPListItemCollection items = null;
			string queryText = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }


				if (itemIds != null && itemIds.Length > 0)
				{
					// query to select all by id
					SPQuery q = new SPQuery();

					foreach (int id in itemIds)
					{
						queryText += "<Value Type=\"Counter\">" + id + "</Value>";
					}
					queryText = "<Where><In><FieldRef Name=\"ID\" /><Values>" + queryText + "</Values></In></Where>";
					
					// order by
					if (orderByFields != null && orderByFields.Count > 0)
					{
						queryText += "<OrderBy>";
						foreach (Caml.OrderByField f in orderByFields)
						{
							if (!string.IsNullOrEmpty(f.FieldName))
							{
								queryText += "<FieldRef Name='" + f.FieldName + "' Ascending='" + !f.IsDescending + "' />";
							}
						}
						queryText += "</OrderBy>";
					}
					q.Query = queryText;

					// view fields
					if (fieldsToReturn != null && fieldsToReturn.Length > 0) { q.ViewFields = Caml.FormatSPQueryViewFields(fieldsToReturn); }

					// call original
					items = GetItems(list, q);
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("drgysreutdr7dsdrrdsz", ex, "CAML: " + queryText + " ids: " 
					+ Utilities.ConvertArrayToString(itemIds));
				items = null;
			}
			return items;
		}

		/// <summary>
		/// Get items by itemIds array passed
		/// </summary>
		/// <param name="list"></param>
		/// <param name="itemIds"></param>
		/// <returns></returns>
		public static SPListItemCollection GetItems(SPList list, int[] itemIds)
		{
			return GetItems(list, itemIds, null, null);
		}
		/// <summary>
		/// Get items by itemIds array passed
		/// </summary>
		/// <param name="list"></param>
		/// <param name="itemIds"></param>
		/// <param name="orderByFields"></param>
		/// <returns></returns>
		public static SPListItemCollection GetItems(SPList list, int[] itemIds, List<Caml.OrderByField> orderByFields)
		{
			return GetItems(list, itemIds, orderByFields, null);
		}
		/// <summary>
		/// Convert SharePoint SPListItemCollection into Generic collection
		/// </summary>
		/// <param name="items"></param>
		/// <returns></returns>
		public static List<SPListItem> ConvertSPListItemCollectionToGenericCollection(SPListItemCollection items)
		{
			List<SPListItem> itemsConverted = null;
			if (items != null)
			{
				itemsConverted = new List<SPListItem>();
				foreach (SPListItem item in items)
				{
					itemsConverted.Add(item);
				}

				if (items.Count != itemsConverted.Count)
				{
					Log.WriteError("57rtythngddsfdsre5ftRew", "Failed to convert SPListItemCollection to generic collection. items.count passed: "
						+ items.Count + ", itemsConverted.Count: " + itemsConverted.Count);
				}
			}
			return itemsConverted;
		}

		

		/// <summary>
		/// Get paginated collection of items
		/// http://khaled-abrass.blogspot.com/2009/10/custom-paging-in-sharepoint-list.html
		/// </summary>
		/// <param name="list">list to get items from</param>
		/// <param name="query">CAML query (no Query tags!)</param>
		/// <param name="fieldsToReturn">item fields to return. Important to specify, makes SQL query more performant</param>
		/// <param name="pageNumber">Which page currently on. If 0 passed, will use page 1</param>
		/// <param name="pageSize">How many items to return. If 0, will not restrict number of items
		/// (in other words, will not paginate)
		/// </param>
		/// <param name="totalItems">total items that query returned, NOT total items returned</param>
		/// <returns></returns>
		public static SPListItemCollection GetItems(SPList list, string query, string[] fieldsToReturn,
			int pageNumber, int pageSize, ref int totalItems)
		{
			SPListItemCollection items = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }
				if (pageNumber == 0) { pageNumber = 1; }


				/// get total number of items (only returns the 
				SPQuery queryTotalItems = new SPQuery();
				if (!string.IsNullOrEmpty(query)) { queryTotalItems.Query = query; }
				queryTotalItems.ViewAttributes = "Scope='RecursiveAll'";
				queryTotalItems.ViewFields = Utilities.FormatSPQueryViewFields(new string[] { "ID" });
				SPListItemCollection totalItemsCollection = list.GetItems(queryTotalItems);
				if (totalItemsCollection != null)
				{
					totalItems = totalItemsCollection.Count;
					totalItemsCollection = null;
				}
				queryTotalItems = null;

				if (totalItems != 0)
				{
					SPListItemCollectionPosition position = null;
					if (pageNumber > 1)
					{
						// get fields being sorted by
						string[] fieldsOrderBy = Caml.GetOrderByFieldsFromQuery(query);

						// get id for last item of previous page
						SPQuery queryPreviousItem = new SPQuery();
						if (!string.IsNullOrEmpty(query)) { queryPreviousItem.Query = query; }
						queryPreviousItem.ViewAttributes = "Scope='RecursiveAll'";
						queryPreviousItem.ViewFields = Utilities.FormatSPQueryViewFields(fieldsOrderBy);
						queryPreviousItem.RowLimit = (uint)(pageSize * (pageNumber - 1));
						SPListItemCollection previousItemsCollection = list.GetItems(queryPreviousItem);
						queryPreviousItem = null;

						// get values for the order by fields
						string orderByFieldValues = string.Empty;
						if (fieldsOrderBy != null && fieldsOrderBy.Length > 0)
						{
							foreach (string f in fieldsOrderBy)
							{
								orderByFieldValues += "&p_" + f + "="
									+ SPHelper.Field.GetTextValue(previousItemsCollection[previousItemsCollection.Count - 1], f);
							}
						}

						// Create Paging Information which will be used for retrieving paging based items
						position = new SPListItemCollectionPosition(
							"Paged=TRUE&p_ID=" + previousItemsCollection[previousItemsCollection.Count - 1].ID
							+ orderByFieldValues);
					}

					// final query
					SPQuery queryFinal = new SPQuery();
					if (!string.IsNullOrEmpty(query)) { queryFinal.Query = query; }
					if (fieldsToReturn != null && fieldsToReturn.Length > 0)
					{
						queryFinal.ViewAttributes = "Scope='RecursiveAll'";
						queryFinal.ViewFields = Utilities.FormatSPQueryViewFields(fieldsToReturn);
					}
					if (pageSize > 0) { queryFinal.RowLimit = Convert.ToUInt32(pageSize); }

					// check if the page index = 1,that mean no need for list position
					if (pageNumber > 1 && position != null)
					{
						queryFinal.ListItemCollectionPosition = position;
					}

					// Execute final query.
					items = list.GetItems(queryFinal);

				} // more than 0 items found
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("ertyfdfds5uietyfthdsfVVsr", ex);
				items = null;
			}
			return items;
		}


		/// <summary>
		/// Get SPListItems that contain the term wssId passed
		/// </summary>
		/// <param name="list"></param>
		/// <param name="wssId"></param>
		/// <returns></returns>
		public static SPListItemCollection GetItemsByTerm(SPList list, string fieldname, int wssId,
			string[] fieldsToReturn, List<Caml.OrderByField> orderByFields)
		{
			SPListItemCollection items = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }
				if (wssId == 0) { throw new Exception("wssId passed is 0"); }
				if (!Field.IsExist(list, fieldname)) { throw new Exception("Invalid fieldname: " + fieldname); }


				// call original
				items = GetItemsByTerms(list, fieldname, new List<int>() { wssId }, fieldsToReturn, orderByFields);

			}
			catch (Exception ex)
			{
				Log.WriteError("dfxhhkvdfhsresrrd", ex, "wssId: " + wssId);
				items = null;
			}
			return items;
		}

		/// <summary>
		/// Get SPListItems that contain the term wssId passed
		/// </summary>
		/// <param name="list"></param>
		/// <param name="fieldname"></param>
		/// <param name="wssId"></param>
		/// <returns></returns>
		public static SPListItemCollection GetItemsByTerm(SPList list, string fieldname, int wssId)
		{
			// call original
			return GetItemsByTerm(list, fieldname, wssId, null, null);
		}

		/// <summary>
		/// Get SPListItems that contain the term passed
		/// </summary>
		/// <param name="list"></param>
		/// <param name="fieldname"></param>
		/// <param name="term"></param>
		/// <returns></returns>
		public static SPListItemCollection GetItemsByTerm(SPList list, string fieldname, Term term)
		{
			SPListItemCollection items = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }
				if (term == null) { throw new Exception("Term passed is null"); }


				// get wssId for term
				int? wssId = Taxonomy.GetWssId(list.ParentWeb, term);
				// if (wssId == null) { throw new Exception("Failed to get wssId for term: " + term.Name); }

				// call original
				if (wssId != null) { items = GetItemsByTerm(list, fieldname, wssId.Value); }
			}
			catch (Exception ex)
			{
				Log.WriteError("ertgrdsswe4a45", ex);
				items = null;
			}
			return items;
		}
		/// <summary>
		/// Get SPListItems that contain the taxonomy field value passed
		/// </summary>
		/// <param name="list"></param>
		/// <param name="fieldname"></param>
		/// <param name="taxonomyFieldValue"></param>
		/// <returns></returns>
		public static SPListItemCollection GetItemsByTerm(SPList list, string fieldname, 
			TaxonomyFieldValue taxonomyFieldValue)
		{
			SPListItemCollection items = null;
			try
			{
				// init
				if (taxonomyFieldValue == null) { throw new Exception("Term passed is null"); }

				// call original
				items = GetItemsByTerm(list, fieldname, taxonomyFieldValue.WssId);
			}
			catch (Exception ex)
			{
				Log.WriteError("tsydhfydfsserd5", ex);
				items = null;
			}
			return items;
		}

		/// <summary>
		/// get multiple items that have one or more of the passed termIds
		/// </summary>
		/// <param name="list"></param>
		/// <param name="fieldname"></param>
		/// <param name="termIds"></param>
		/// <param name="fieldsToReturn"></param>
		/// <param name="orderBy"></param>
		/// <returns></returns>
		public static SPListItemCollection GetItemsByTerms(SPList list, string fieldname, List<int> wssIds,
			string[] fieldsToReturn, List<Caml.OrderByField> orderByFields)
		{
			SPListItemCollection items = null;
			try 
			{	        
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }
				if (!Field.IsExist(list, fieldname)) { throw new Exception("Invalid fieldname: " + fieldname); }

				if (wssIds != null && wssIds.Count > 0)
				{
					// query
					SPQuery q = new SPQuery();
					q.Query = "<Where><In><FieldRef Name='" + fieldname + "' LookupId='TRUE' /><Values>";
					foreach (int wssId in wssIds)
					{
						q.Query += "<Value Type='Integer'>" + wssId + "</Value>";
					}
					q.Query += "</Values></In></Where>";

					// fields
					if (fieldsToReturn != null && fieldsToReturn.Length > 0)
					{
						q.ViewFields = Caml.FormatSPQueryViewFields(fieldsToReturn);
					}

					// order by
					if (orderByFields != null && orderByFields.Count > 0)
					{
						q.Query += Caml.FormatOrderBy(orderByFields);
					}

					// fetch
					items = list.GetItems(q);
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("6e5r7tydrhgxdfddxgdgdgx", ex);
			}
			return items;
		}

		/// <summary>
		/// Get many items based on termIds passed
		/// </summary>
		/// <param name="list"></param>
		/// <param name="fieldname"></param>
		/// <param name="termIds"></param>
		/// <param name="fieldsToReturn"></param>
		/// <param name="orderByFields"></param>
		/// <returns></returns>
		public static SPListItemCollection GetItemsByTerms(SPList list, string fieldname, List<Guid> termIds,
			string[] fieldsToReturn, List<Caml.OrderByField> orderByFields)
		{
			try 
			{
				// get wssIds
				SPWeb rootWeb = null;
				if (SPContext.Current != null && SPContext.Current.Site != null) 
				{ 
					rootWeb = SPContext.Current.Site.RootWeb; 
				}
				else if (list != null && list.ParentWeb != null && list.ParentWeb.Site != null)
				{
					rootWeb = list.ParentWeb.Site.RootWeb;
				}
				var wssIds = Taxonomy.GetWssIds(rootWeb, termIds);


				// call original
				return GetItemsByTerms(list, fieldname, wssIds, fieldsToReturn, orderByFields);
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("6e5eryhdsfxcvsZdfxghcffcjcg", ex);
				return null;
			}
		}

		/// <summary>
		/// Get many items by many passed taxonomy terms
		/// </summary>
		/// <param name="list"></param>
		/// <param name="fieldname"></param>
		/// <param name="taxonomyFieldValue"></param>
		/// <param name="fieldsToReturn"></param>
		/// <param name="orderByFields"></param>
		/// <returns></returns>
		public static SPListItemCollection GetItemsByTerms(SPList list, string fieldname, 
			ICollection<TaxonomyFieldValue> taxonomyFieldValue, string[] fieldsToReturn, 
			List<Caml.OrderByField> orderByFields)
		{
			if (taxonomyFieldValue != null)
			{
				var wssIds = new List<int>();
				foreach (TaxonomyFieldValue t in taxonomyFieldValue)
				{
					wssIds.Add(t.WssId);
				}

				// call original
				return GetItemsByTerms(list, fieldname, wssIds, fieldsToReturn, orderByFields);
			}
			return null;
		}


		/// <summary>
		/// Return SPListItems that have passed itemId in their lookup field
		/// </summary>
		/// <param name="list"></param>
		/// <param name="lookupFieldname"></param>
		/// <param name="lookupFieldIds"></param>
		/// <param name="fieldsToReturn"></param>
		/// <param name="orderByFields"></param>
		/// <returns></returns>
		public static SPListItemCollection GetItemsByLookupField(SPList list, string lookupFieldname, List<int> lookupFieldIds,
			string[] fieldsToReturn, List<Caml.OrderByField> orderByFields)
		{
			SPListItemCollection items = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }
				if (!Field.IsExist(list, lookupFieldname)) { throw new Exception("Invalid fieldname: " + lookupFieldname); }

				if (lookupFieldIds != null && lookupFieldIds.Count > 0)
				{
					// query
					SPQuery q = new SPQuery();
					q.Query = "<Where><In><FieldRef Name='" + lookupFieldname + "' LookupId='TRUE' /><Values>";
					foreach (int id in lookupFieldIds)
					{
						q.Query += "<Value Type='Integer'>" + id + "</Value>";
					}
					q.Query += "</Values></In></Where>";

					// fields
					if (fieldsToReturn != null && fieldsToReturn.Length > 0)
					{
						q.ViewFields = Caml.FormatSPQueryViewFields(fieldsToReturn);
					}

					// order by
					if (orderByFields != null && orderByFields.Count > 0)
					{
						q.Query += Caml.FormatOrderBy(orderByFields);
					}

					// fetch
					items = list.GetItems(q);
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("e56jygtdrhsreersers", ex);
			}
			return items;
		}

		/// <summary>
		/// Save one SPListItem
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public static bool Save(SPListItem item)
		{
			bool isSuccess = false;
			try
			{
				if (item == null) { throw new Exception("SPListItem passed is null"); }
				List<SPListItem> itemsToSave = new List<SPListItem>(); itemsToSave.Add(item);
				List<SPListItem> itemsFailedToSave = new List<SPListItem>();
				Save(itemsToSave, false, ref itemsFailedToSave);

				if (itemsFailedToSave.Count > 0) { isSuccess = false; }
				else { isSuccess = true; }
			}
			catch (Exception ex)
			{
				string title = item == null ? "Null SPListItem" : item.Title;
				Log.WriteError("re65tyfdtrsreestsgdrstrtt", ex, "SPListItem.Title: " + title);
				isSuccess = false;
			}
			return isSuccess;
		}

		/// <summary>
		/// Save multiple items. If any item fails, then returns false.
		/// </summary>
		/// <param name="items"></param>
		/// <returns></returns>
		public static bool Save(List<SPListItem> items)
		{
			bool isSuccess = false;
			try
			{
				List<SPListItem> itemsFailedToSave = new List<SPListItem>();
				Save(items, false, ref itemsFailedToSave);

				if (itemsFailedToSave.Count > 0) { isSuccess = false; }
				else { isSuccess = true; }

			}
			catch (Exception ex)
			{
				Log.WriteError("6ur8ykguffdseer", ex);
				isSuccess = false;
			}
			return isSuccess;
		}

		/// <summary>
		/// Save many SPListItems
		/// </summary>
		/// <param name="itemsToSave"></param>
		/// <param name="itemsFailedToSave"></param>
		public static void Save(List<SPListItem> itemsToSave, bool isRemoveItemsFailedToSave, 
			ref List<SPListItem> itemsFailedToSave)
		{
			string currentItem = null;
			try
			{
				if (itemsToSave == null) { throw new Exception("Passed an empty collection of SPListItems"); }

				if (itemsToSave.Count > 0)
				{
					foreach (SPListItem item in itemsToSave)
					{
						currentItem = item.Name;
						try
						{
							item.Update();
						}
						catch (Exception ex)
						{
							if (itemsFailedToSave == null) { itemsFailedToSave = new List<SPListItem>(); }
							itemsFailedToSave.Add(item);
							Log.WriteError("dtrufgdstyrerse5", "Failed to save item: " + currentItem + ". Error: " + ex.Message + " \n\n\n" + ex.ToString());
						}
					}

					// remove documents that failed to save
					if (isRemoveItemsFailedToSave && itemsFailedToSave != null && itemsFailedToSave.Count > 0)
					{
						for (int i = 0; i < itemsToSave.Count; i++)
						{
							bool isMatch = false;
							foreach (SPListItem item in itemsFailedToSave)
							{
								if (itemsToSave[i] == item)
								{
									isMatch = true;
									break;
								}
							}
							if (isMatch)
							{
								itemsToSave.RemoveAt(i);
								i--;
							}
						}
					} // remove items that failed to save
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("t7rrjtcxgfbcxffxd", ex, "current item: " + currentItem);
			}
		}

		/// <summary>
		/// Deletes many items in bulk
		/// Does not delete checked out items
		/// Does not delete homepage in a Pages library
		/// http://msdn.microsoft.com/en-us/library/ms461938.aspx
		/// http://social.technet.microsoft.com/Forums/en-US/sharepoint2010customization/thread/e0eefd6b-e954-46d8-9c49-703de6eda592
		/// </summary>
		/// <param name="list">List to delete all items from</param>
		/// <param name="isDisableRecycleBin">If disabled, all existing items in recycle bin will be deleted!!!!</param>
		/// <returns></returns>
		public static bool BulkDeleteItems(SPList list, SPListItemCollection items, bool isDisableRecycleBin)
		{
			bool isSuccess = true;
			try
			{
				// init
				if (list == null) { throw new Exception("SPlist passed is null"); }
				if (items == null) { throw new Exception("SPLitItems collection passed is null"); }

				if (items.Count > 0)
				{
					// important, this will ALSO delete all existing recycle bin items!!!!!
					if (isDisableRecycleBin && list.ParentWeb.Site.WebApplication.RecycleBinEnabled) 
					{ list.ParentWeb.Site.WebApplication.RecycleBinEnabled = false; }
					list.ParentWeb.AllowUnsafeUpdates = true;

					string deleteCamlQuery = null;
					string camlStart = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Batch>";
					string camlEnd = "</Batch>";
					string command = "<Method><SetList Scope=\"Request\">" + list.ID + "</SetList>"
						+ "<SetVar Name=\"ID\">{0}</SetVar>"
						+ "<SetVar Name=\"Cmd\">Delete</SetVar>";
					if (list.BaseType == SPBaseType.DocumentLibrary)
					{
						// for document libraries, additional owsfileref parameter is required
						command += "<SetVar Name=\"owsfileref\">{1}</SetVar>";
					}
					command += "</Method>";

					int numberItemsToBatchDelete = 0;
					for (int i = (items.Count - 1); i > -1; i--)
					{
						if (list.BaseType == SPBaseType.DocumentLibrary)
						{
							// format delete command for files
							deleteCamlQuery += string.Format(command, items[i].ID.ToString(), items[i].File.ServerRelativeUrl);
						}
						else
						{
							// format delete command for items
							deleteCamlQuery += string.Format(command, items[i].ID.ToString());
						}
						numberItemsToBatchDelete++;

						// delete batch (every 100, or if last one)
						if (numberItemsToBatchDelete > 99 || i == 0)
						{
							deleteCamlQuery = camlStart + deleteCamlQuery + camlEnd;
							var results = list.ParentWeb.ProcessBatchData(deleteCamlQuery);

							// check to see if any errors returned
							if (results.Contains("<ErrorText>")) { isSuccess = false; }

							// delete is successful, reset batch delete index and clear caml
							deleteCamlQuery = null;
							numberItemsToBatchDelete = 0;
						}
					}
				} // has items?
			}
			catch (Exception ex)
			{
				Log.WriteError("iukyufydtrt65tyrtdgdsffdr", ex);
				isSuccess = false;
			}
			finally
			{
				// re-enable recylce bin
				if (isDisableRecycleBin && list != null && !list.ParentWeb.Site.WebApplication.RecycleBinEnabled) 
				{ list.ParentWeb.Site.WebApplication.RecycleBinEnabled = true; }
				list.ParentWeb.AllowUnsafeUpdates = false;
			}
			return isSuccess;
		}

		/// <summary>
		/// Delete all items in passed list, using bulk delete
		/// Will not delete homepage in Pages library or any checked out items
		/// </summary>
		/// <param name="list"></param>
		/// <param name="isDisableRecycleBin"></param>
		/// <returns></returns>
		public static bool DeleteAllItems(SPList list, bool isDisableRecycleBin)
		{
			bool isSuccess = false;
			try
			{
				if (list == null) { throw new Exception("SPList passed is null"); }
				isSuccess = BulkDeleteItems(list, list.Items, isDisableRecycleBin);
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("78ti6ry5esrggsdgssgr", ex);
				isSuccess = false;
			}
			return isSuccess;
		}
		










//        public class Attachment
//        {
//            public enum ImageSize { small, medium };
//            public string Filename { get; set; }
//            public string Directory { get; set; }
//            public string Ext { get; set; }
//            public string UrlHtmlEncoded { get; set; }
//            public string GetImageFilename(ImageSize imageSize)
//            {
//                string o = null;
//                if (!string.IsNullOrEmpty(Ext))
//                {
//                    o = "icon-" + Ext.ToLower().Trim() + "-";
//                    if (imageSize == ImageSize.small) { o += "16"; }
//                    else if (imageSize == ImageSize.medium) { o += "32"; }
//                    o += ".gif";
//                }
//                return o;
//            }
//            public string GetImageHtml(string anchorStyle, ImageSize imageSize)
//            {
//                string o = null;
//                if (!string.IsNullOrEmpty(UrlHtmlEncoded))
//                {
//                    o = string.Format(@"<a href='{0}' Title='Click to View File' style='{1}'>
//						<img src='/images/template/{2}' style='border:0px; margin:0px 5px 0px 0px;' alt='-' />{3}</a>",
//                        UrlHtmlEncoded, anchorStyle, GetImageFilename(imageSize), Filename);
//                }
//                return o;
//            }
//        }

		/// <summary>
		/// Get SPFile attachments for passed item
		/// http://www.binarywave.com/blogs/eshupps/Lists/Posts/Post.aspx?List=89cbe813-99f7-4257-a23a-5fefc377336b&ID=26&Web=c7893495-be3b-4d73-9875-28b039760651
		/// </summary>
		/// <param name="item"></param>
		public static SPFileCollection GetAttachments(SPListItem item)
		{
			SPFileCollection files = null;
			try
			{
				// init
				if (item == null) { throw new Exception("SPListItem passed is null"); }
				if (item.ParentList == null || item.ParentList.RootFolder == null) { throw new Exception("SPListItem passed does not have SPList prefetched"); }


				SPFolder folder = item.ParentList.RootFolder.SubFolders["Attachments"].SubFolders[item.ID.ToString()];
				if (folder != null && folder.Files != null) { files = folder.Files; }
			}
			catch (Exception ex)
			{
				Log.WriteError("srydytidruseyey", ex);
				files = null;
			}
			return files;
		}


		///// <summary>
		///// Get collection of attachment information from passed list
		///// Does NOT get the SPFile objects
		///// </summary>
		///// <param name="item"></param>
		///// <returns></returns>
		//public static List<Attachment> GetAttachmentsHtml(SPListItem item)
		//{
		//    List<Attachment> attachments = null;
		//    try
		//    {
		//        // init
		//        if (item == null) { throw new Exception("SPListItem passed is null"); }
		//        attachments = new List<Attachment>();


		//        if (item.Attachments != null && item.Attachments.Count > 0)
		//        {
		//            foreach (string filename in item.Attachments)
		//            {
		//                Attachment a = new Attachment();
		//                a.Filename = filename;
		//                a.Directory = item.Attachments.UrlPrefix;
		//                if (a.Directory.Substring(a.Directory.Length - 1) != "/") { a.Directory += "/"; }
		//                a.Ext = a.Filename.Substring(a.Filename.LastIndexOf(".") + 1);
		//                a.UrlHtmlEncoded = a.Directory + a.Filename;
		//                attachments.Add(a);
		//            }
		//        }
		//    }
		//    catch (Exception ex)
		//    {
		//        Log.WriteError("dfhgyjdsrdysrdgfy", ex);
		//        attachments = null;
		//    }
		//    return attachments;
		//}

		/// <summary>
		/// Get just the attachment filenames for passed list item
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public static List<string> GetAttachmentFilenames(SPListItem item)
		{
			List<string> filenames = null;
			try
			{
				// init
				if (item == null) { throw new Exception("SPListItem passed is null"); }
				filenames = new List<string>();

				if (item.Attachments != null && item.Attachments.Count > 0)
				{
					foreach (string filename in item.Attachments)
					{
						filenames.Add(filename);
					}
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("sdtrygfydtsuersruxgf6rgcf", ex);
				filenames = null;
			}
			return filenames;
		}

		/// <summary>
		/// Get collection of attachment urls from passed listItem
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public static List<string> GetAttachmentUrls(SPListItem item)
		{
			List<string> urls = null;
			try
			{
				// init
				if (item == null) { throw new Exception("SPListItem passed is null"); }
				urls = new List<string>();

				if (item.Attachments != null && item.Attachments.Count > 0)
				{
					foreach (string filename in item.Attachments)
					{
						urls.Add(item.Attachments.UrlPrefix + filename);
					}
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("6e5idjthssdrrdst", ex);
				urls = null;
			}
			return urls;
		}

		/// <summary>
		/// Get attachments as collection of SPFiles from passed SPListItem
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public static List<SPFile> GetAttachmentFiles(SPListItem item)
		{
			List<SPFile> attachments = null;
			try
			{
				// init
				if (item == null) { throw new Exception("SPListItem passed is null"); }
				attachments = new List<SPFile>();

				if (item.Attachments != null && item.Attachments.Count > 0)
				{
					foreach (string filename in item.Attachments)
					{
						attachments.Add(item.Web.GetFile(item.Attachments.UrlPrefix + filename));
					}
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("rs6yu5rhseerssrt", ex);
				attachments = null;
			}
			return attachments;
		}


		/// <summary>
		/// Default list of system lists that are not hidden and usually don't need to get the recent changes from
		/// </summary>
		public static List<string> RecentChangesSystemListsToExclude
		{ 
			get
			{
				return new List<string>()
				{
					{ "Content and Structure Reports" },
					{ "Customized Reports" },
					{ "Form Templates" },
					{ "Reusable Content" },
					{ "Site Assets" },
					{ "Site Collection Images" },
					{ "Style Library" },
					{ "Whereabouts" }
				};
			}
		}


		/// <summary>
		/// Get all items that have recently changed/created
		/// </summary>
		/// <param name="list"></param>
		/// <param name="contentTypesToInclude">Only types listed here will be excluded. If null, includes all</param>
		/// <param name="contentTypesToExclude">These types will be excluded</param>
		/// <param name="itemMinimumDate">IF null, will get all items</param>
		/// <returns></returns>
		public static List<SPListItem> RecentChanges(SPList list, string[] contentTypesToInclude, string[] contentTypesToExclude, DateTime? itemMinimumDate)
		{
			string currentListUrl = null;
			string currentItemUrl = null;
			List<SPListItem> items = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }
				currentListUrl = List.GetUrl(list);
				items = new List<SPListItem>();

				
				// get list items
				foreach (SPListItem item in list.Items)
				{
					currentItemUrl = item.Url;
					if (isIncludeItem(item, contentTypesToInclude, contentTypesToExclude, itemMinimumDate))
					{
						items.Add(item);
					}
					currentItemUrl = null;
				}

				// get list folders
				if (list.Folders != null)
				{
					foreach (SPListItem folder in list.Folders)
					{
						currentItemUrl = folder.Url;
						if (isIncludeItem(folder, contentTypesToInclude, contentTypesToExclude, itemMinimumDate))
						{
							items.Add(folder);
						}
						currentItemUrl = null;
					}
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("8yigfrftsdeddfyuttf", ex, 
					"list url: " + currentListUrl + ", item url: " + currentItemUrl);
				items = null;
			}
			return items;
		}

		/// <summary>
		/// Get list of recently changed/created SPList items from all lists of passed web
		/// </summary>
		/// <param name="web"></param>
		/// <param name="listsToInclude"></param>
		/// <param name="listsToExclude"></param>
		/// <param name="listBaseTemplatesToExclude">List base templates to exclude, such as "Tasks", "Events", etc.</param>
		/// /// <param name="listBaseTemplatesToInclude">List templates types to include, such as "Tasks", "Events", etc.</param>
		/// <param name="contentTypesToExclude">Content types such as "Item" "Calendar" "Task"</param>
		/// <param name="itemMinimumDate"></param>
		/// <returns></returns>
		public static List<SPListItem> RecentChanges(SPWeb web, bool isIncludeChildWebs, string[] listsToInclude, string[] listsToExclude,
			string[] listBaseTemplatesToInclude, string[] listBaseTemplatesToExclude, 
			string[] contentTypesToInclude, string[] contentTypesToExclude, DateTime? itemMinimumDate)
		{
			string currentWebUrl = null;
			string currentListUrl = null;
			List<SPListItem> items = null;
			try
			{
				// init
				if (web == null) { throw new Exception("SPWeb passed is null"); }
				currentWebUrl = web.Url;
				items = new List<SPListItem>();


				foreach (SPList list in web.Lists)
				{
					currentListUrl = list.DefaultViewUrl;

					// determine if this list should be included
					bool isIncludeList = true;

					////////////////////////////////////////////////////////////////////////////////////
					// hidden lists are excluded by default
					if (list.Hidden) { isIncludeList = false; }

					////////////////////////////////////////////////////////////////////////////////////
					// include list titles
					if (!isIncludeList && listsToInclude != null && listsToInclude.Length > 0)
					{
						isIncludeList = false;
						foreach (string listTitle in listsToInclude)
						{
							if (listTitle.ToLower().Trim() == list.Title.ToLower().Trim())
							{
								isIncludeList = true;
								break;
							}
						}
					}

					////////////////////////////////////////////////////////////////////////////////////
					// exclude list titles
					if (isIncludeList && listsToExclude != null && listsToExclude.Length > 0)
					{
						foreach (string listTitle in listsToExclude)
						{
							if (listTitle.ToLower().Trim() == list.Title.ToLower().Trim())
							{
								isIncludeList = false;
								break;
							}
						}
					}

					////////////////////////////////////////////////////////////////////////////////////
					// include list base template
					if (!isIncludeList && listBaseTemplatesToInclude != null && listBaseTemplatesToInclude.Length > 0)
					{
						isIncludeList = false;
						string listBaseTemplate = list.BaseTemplate.ToString().ToLower().Trim();
						foreach (string listBaseTemplateToInclude in listBaseTemplatesToInclude)
						{
							if (listBaseTemplateToInclude.ToLower().Trim() == listBaseTemplate)
							{
								isIncludeList = true;
								break;
							}
						}
					}

					////////////////////////////////////////////////////////////////////////////////////
					// exclude list base template
					if (isIncludeList && listBaseTemplatesToExclude != null && listBaseTemplatesToExclude.Length > 0)
					{
						string listBaseTemplate = list.BaseTemplate.ToString().ToLower().Trim();
						foreach (string listBaseTemplateToExclude in listBaseTemplatesToExclude)
						{
							if (listBaseTemplateToExclude.ToLower().Trim() == listBaseTemplate)
							{
								isIncludeList = false;
								break;
							}
						}
					}


					// scan list for changes
					if (isIncludeList)
					{
						List<SPListItem> _items = RecentChanges(list, contentTypesToInclude, contentTypesToExclude, itemMinimumDate);
						if (_items != null && _items.Count > 0) { items.AddRange(_items); }
					}

					currentListUrl = null;
				} // foreach list


				// child webs?
				if (isIncludeChildWebs && web.Webs.Count > 0)
				{
					foreach (SPWeb childWeb in web.Webs)
					{
						// call self
						List<SPListItem> childItems = RecentChanges(childWeb, isIncludeChildWebs, listsToInclude, listsToExclude,
							listBaseTemplatesToInclude, listBaseTemplatesToExclude, 
							contentTypesToInclude, contentTypesToExclude, itemMinimumDate);
						if (childItems == null) { Log.WriteError("u6r7i6tytfdrstdsrt", "Failed to get child items for childWeb: " + childWeb.Url); }

						// add to master list
						if (childItems != null && childItems.Count > 0) { items.AddRange(childItems); }
					}
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("6ui7yuytsdeddfyuttf", ex,
					"web url: " + currentWebUrl + ", list url: " + currentListUrl);
				items = null;
			}
			return items;
		}

		/// <summary>
		/// private filter used for determing which recent items to get
		/// </summary>
		/// <param name="item"></param>
		/// <param name="contentTypesToInclude"></param>
		/// <param name="contentTypesToExclude"></param>
		/// <param name="itemMinimumDate"></param>
		/// <returns></returns>
		private static bool isIncludeItem(SPListItem item, string[] contentTypesToInclude, string[] contentTypesToExclude,
			DateTime? itemMinimumDate)
		{
			bool _isIncludeItem = true;
			if (item == null) { _isIncludeItem = false; }
			else
			{
				// content type
				if ((contentTypesToInclude != null && contentTypesToInclude.Length > 0) ||
					(contentTypesToExclude != null && contentTypesToExclude.Length > 0)
					)
				{
					string contentTypeName = SPHelper.ContentType.GetContentTypeName(item);
					if (contentTypesToInclude != null && contentTypesToInclude.Length > 0)
					{
						_isIncludeItem = false;
						foreach (string contentTypetoInclude in contentTypesToInclude)
						{
							if (contentTypetoInclude == contentTypeName)
							{
								_isIncludeItem = true;
								break;
							}
						}
					}
					if (_isIncludeItem && (contentTypesToExclude != null && contentTypesToExclude.Length > 0))
					{
						foreach (string contentTypetoExclude in contentTypesToExclude)
						{
							if (contentTypetoExclude == contentTypeName)
							{
								_isIncludeItem = false;
								break;
							}
						}
					}
				}

				// date filter
				if (_isIncludeItem && itemMinimumDate != null && itemMinimumDate != DateTime.MinValue)
				{
					_isIncludeItem = false;
					DateTime? createdDate = null; DateTime? modifiedDate = null;
					try
					{
						if (item["Created"] != null) { createdDate = item["Created"] as DateTime?; }
						if (item["Modified"] != null) { modifiedDate = item["Modified"] as DateTime?; }
					}
					catch (Exception ex)
					{
						string itemUrl = null; if (item != null) { itemUrl = item.Url; }
						SPHelper.Log.WriteError("6ujdstGGsdgrses", ex, "item url: " + itemUrl);
					}
					if ((createdDate != null && createdDate >= itemMinimumDate) ||
						(modifiedDate != null && modifiedDate >= itemMinimumDate)
						)
					{
						_isIncludeItem = true;
					}
				}
			}
			return _isIncludeItem;
		}


		/// <summary>
		/// Gets an item's icon Url (relative link) and the item's extension
		/// </summary>
		/// <param name="item"></param>
		/// <param name="itemExt"></param>
		/// <returns></returns>
		public static string GetIconUrl(SPListItem item, ref string itemExt)
		{
			string o = null;
			try 
			{	        
				// init
				if (item == null) { throw new Exception("SPListItem passed is null"); }
				string filename = null;


				// get item content type
				string contentTypeName = ContentType.GetContentTypeName(item);

				// get item extenstion
				itemExt = File.GetExtension(item);

				// get icon filename
				if (item.File != null) { filename = item.File.IconUrl; }
				
				if (contentTypeName == "Link")
				{
					itemExt = "url";
					filename = "ICHTM.GIF";
				}
				else if (contentTypeName == "Event")
				{
					itemExt = "Event";
					filename = "ITEVENT.GIF";
				}
				else if (contentTypeName == "Contact")
				{
					itemExt = "Contact";
					filename = "ITCONTCT.GIF";
				}
				else if (contentTypeName == "Message" || contentTypeName == "Discussion")
				{
					itemExt = "Discussion";
					filename = "ITDISC.GIF";
				}
				else if (contentTypeName == "Task" || contentTypeName == "Project Task")
				{
					itemExt = "Task";
					filename = "ITTASK.PNG";
				}
				else if (contentTypeName == "Issue")
				{
					itemExt = "Issue";
					filename = "ITISSUE.PNG";
				}

				// default icon filename
				if (string.IsNullOrEmpty(filename)) { filename = "icgen.gif"; }

				// prepend relative url
				o = "/_layouts/images/" + filename;
		
			}
			catch (Exception ex)
			{
				Log.WriteError("drstyygiuyufgdtrydrtdtrdfxcf", ex);
				o = null;
			}
			return o;
		}


		/// <summary>
		/// Gets an item's icon Url (relative link)
		/// </summary>
		/// <param name="item"></param>
		/// <param name="itemExt"></param>
		/// <returns></returns>
		public static string GetIconUrl(SPListItem item)
		{
			string devNull = null;
			// call original
			return GetIconUrl(item, ref devNull);
		}

		/// <summary>
		/// Get Item url
		/// </summary>
		/// <param name="item"></param>
		/// <param name="isUseDisplayFormForNonFiles"></param>
		/// <param name="isUseLinkContentTypeUrlValue">If true and passed item is of link content type, then display link's url, not url to the link itme itself</param>
		/// <returns></returns>
		public static string GetUrl(SPListItem item, bool isAbsoluteUrl, bool isUseLinkContentTypeUrlValue)
		{
			string url = null;
			try
			{
				// init
				if (item == null) { throw new Exception("SPListItem passed is null"); }

				// get url from SPListItem of Link content type?
				string contentTypeName = null;
				if (isUseLinkContentTypeUrlValue) { contentTypeName = SPHelper.ContentType.GetContentTypeName(item); }
				if (isUseLinkContentTypeUrlValue && !string.IsNullOrEmpty(contentTypeName) && contentTypeName == "Link")
				{
					url = SPHelper.Field.GetUrlOnlyValue(item, "URL");
					if (string.IsNullOrEmpty(url))
					{
						// get url to the links list instead
						url = SPHelper.List.GetUrl(item.ParentList, isAbsoluteUrl);
					}
				}
				else
				{
					// parent list url
					string parentWebUrl = SPHelper.Web.GetUrl(item.Web, isAbsoluteUrl);
					if (!string.IsNullOrEmpty(parentWebUrl) && parentWebUrl.Substring(parentWebUrl.Length - 1) == "/")
					{
						// remove trailing slash, i'll add it later
						parentWebUrl = parentWebUrl.Substring(0, parentWebUrl.Length - 1);
					}

					// item url
					string itemUrl = item.Url;

					// update item url if has a .000 extention, which is an internal url or if list item
					if (item.File == null ||
						(!string.IsNullOrEmpty(itemUrl) && itemUrl.Length > 4 
						&& itemUrl.Substring(itemUrl.Length - 4) == ".000"))
					{
						itemUrl = item.ParentList.Forms[PAGETYPE.PAGE_DISPLAYFORM].Url + "?ID=" + item.ID;
					}
					if (!string.IsNullOrEmpty(itemUrl) && itemUrl.Substring(0, 1) == "/")
					{
						// remove preceeding slash, i'll add later
						itemUrl = itemUrl.Substring(1);
					}

					// combine with item url
					url = parentWebUrl + "/" + itemUrl;

				} // not a link item or ignoring link items
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("trztygiyfuyfurHtftrt5", ex);
				url = null;
			}
			return url;
		}

		/// <summary>
		/// Get item url (server relative)
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public static string GetUrl(SPListItem item)
		{
			return GetUrl(item, false, false);
		}

		/// <summary>
		/// Get the item edit link to edit all the properties for the page, item, or file
		/// (for an SPFile, returns the file properties edit link, not a link to edit the file actual)
		/// </summary>
		/// <param name="item"></param>
		/// <param name="isServerRelativeUrl"></param>
		/// <returns></returns>
		public static string GetEditUrl(SPListItem item, bool isAbsoluteUrl)
		{
			string url = null;
			try
			{
				if (item != null)
				{
					// parent list url
					string parentWebUrl = Web.GetUrl(item.Web, isAbsoluteUrl);
					if (!string.IsNullOrEmpty(parentWebUrl) && parentWebUrl.Substring(parentWebUrl.Length - 1) == "/")
					{
						// remove trailing slash, i'll add it later
						parentWebUrl = parentWebUrl.Substring(0, parentWebUrl.Length - 1);
					}

					// item url
					string itemUrl = item.ParentList.Forms[PAGETYPE.PAGE_EDITFORM].Url + "?ID=" + item.ID;
						// 	url += "EditForm.aspx?ID=" + item.ID; 
						// else { url += "Forms/EditForm.aspx?ID=" + item.ID; }
					if (!string.IsNullOrEmpty(itemUrl) && itemUrl.Substring(itemUrl.Length - 1) == "/")
					{
						// remove trailing slash, i'll add it later
						itemUrl = itemUrl.Substring(0, itemUrl.Length - 1);
					}

					// combine with item url
					url = parentWebUrl + "/" + itemUrl;
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("6uyfhfdfdTHYfdfydfutfu", ex);
			}
			return url;
		}

		/// <summary>
		/// Get edit url for passed item, page, or file
		/// (for an SPFile, returns the file properties edit link, not a link to edit the file actual)
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public static string GetEditUrl(SPListItem item)
		{
			// call original
			return GetEditUrl(item, false);
		}

		/// <summary>
		/// Format item title. If title null, use name, then display name, then "(no title)"
		/// For documents, will get the name instead
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public static string GetTitle(SPListItem item, bool isHttpEncode, int? maxLength)
		{
			string o = null;
			try
			{
				// init
				if (item == null) { throw new Exception("SPListItem passed is null"); }

				// get content type
				string contentTypeName = ContentType.GetContentTypeName(item);

				// get title (for documents, use the name property instead)
				if (contentTypeName == "Document") { o = item.Name; }
				else
				{
					try { o = item.Title; } catch { }
				}
				if (string.IsNullOrEmpty(o)) { o = item.Name; }
				if (string.IsNullOrEmpty(o)) { o = item.DisplayName; }
				if (string.IsNullOrEmpty(o)) { o = "(no title)"; }
				if (contentTypeName == "Message") { o = "Reply: " + o; }
				else if (contentTypeName == "Survey") { o = "Survey: " + o; }

				// truncate?
				if (maxLength != null)
				{
					o = Utilities.Truncate(o, (int)maxLength);
				}

				// encode?
				if (isHttpEncode) { o = System.Web.HttpUtility.HtmlEncode(o); }
			}
			catch (Exception ex)
			{
				Log.WriteError("drttyiHyssdfcvvxfc", ex);
				o = "(no title)";
			}
			return o;
		}

		/// <summary>
		/// Get item title, if null, then try name, display name, then "(no title)"
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public static string GetTitle(SPListItem item)
		{
			// call original
			return GetTitle(item, false, null);
		}

		/// <summary>
		/// Copy an item to a destination list
		/// http://www.communardo.de/techblog/2008/01/08/sharepoint-listenelement-splistitem-in-eine-andere-liste-kopieren/
		/// </summary>
		/// <param name="sourceItem"></param>
		/// <param name="destinationList"></param>
		/// <returns></returns>
		public static SPListItem Copy(SPListItem sourceItem, SPList destinationList, ref List<string> failedToCopyFields, 
			bool isSave)
		{
			SPListItem destinationItem = null;
			try
			{
				// init
				if (sourceItem == null) { throw new Exception("Source SPListItem passed is null"); }
				if (destinationList == null) { throw new Exception("Destination SPList passed is null"); }
				if (failedToCopyFields == null) { failedToCopyFields = new List<string>(); }


				// create destination item
				destinationItem = destinationList.Items.Add();

				// copy properties over to new item
				foreach (SPField f in sourceItem.Fields)
				{
					if (!f.ReadOnlyField && f.InternalName != "Attachments")
					{
						try
						{
							destinationItem[f.InternalName] = sourceItem[f.InternalName];
						}
						catch
						{
							failedToCopyFields.Add(f.InternalName);
						}
					}
				}

				// copy attachments
				if (sourceItem.Attachments != null)
				{
					foreach (string fileName in sourceItem.Attachments)
					{
						try
						{
							SPFile file = sourceItem.ParentList.ParentWeb.GetFile(sourceItem.Attachments.UrlPrefix + fileName);
							byte[] data = file.OpenBinary();
							destinationItem.Attachments.Add(fileName, data);
						}
						catch
						{
							failedToCopyFields.Add(sourceItem.Attachments.UrlPrefix + fileName);
						}
					}
				}

				// save
				if (isSave) { destinationItem.Update(); }
			}
			catch (Exception ex)
			{
				Log.WriteError("vgchhjgfGGgJdgrdst", ex);
				destinationItem = null;
			}
			return destinationItem;
		}



		public static List<SPListItem> RemoveDuplicates(SPListItemCollection items, ref List<SPListItem> duplicateItems)
		{
			var itemsNoDups = new List<SPListItem>();
			try
			{
				// init
				if (duplicateItems == null) { duplicateItems = new List<SPListItem>(); }

				// remove dups
				if (items != null && items.Count > 0)
				{
					foreach (SPListItem item in items)
					{
						if (!itemsNoDups.Contains(item)) { itemsNoDups.Add(item); }
						else { duplicateItems.Add(item); }
					}
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("65789trdfgdfxgtdtg", ex);
			}
			return itemsNoDups;
		}

		public static List<SPListItem> RemoveDuplicates(SPListItemCollection items)
		{
			var dups = new List<SPListItem>();
			// call original
			return RemoveDuplicates(items, ref dups);
		}

		public static List<SPListItem> RemoveDuplicates(List<SPListItem> items, ref List<SPListItem> duplicateItems)
		{
			var itemsNoDups = new List<SPListItem>();
			try
			{
				// init
				if (duplicateItems == null) { duplicateItems = new List<SPListItem>(); }

				// remove dups
				if (items != null && items.Count > 0)
				{
					foreach (SPListItem item in items)
					{
						if (!itemsNoDups.Contains(item)) { itemsNoDups.Add(item); }
						else { duplicateItems.Add(item); }
					}
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("tyihghgfdtfyygugyueer", ex);
			}
			return itemsNoDups;
		}

		public static List<SPListItem> RemoveDuplicates(List<SPListItem> items)
		{
			var dups = new List<SPListItem>();
			// call original
			return RemoveDuplicates(items, ref dups);
		}

		/// <summary>
		/// Get SPListItem by url
		/// </summary>
		/// <param name="url"></param>
		/// <param name="site"></param>
		/// <returns></returns>
		public static SPListItem GetItemByUrl(string url, SPSite site)
		{
			SPListItem item = null;
			try
			{
				// init
				if (string.IsNullOrEmpty(url)) { throw new Exception("Url passed is null"); }
				if (site == null) { site = SPHelper.Site.GetSite(); }
				if (site == null) { throw new Exception("Failed to get SPSite from SPContext"); }


				// get web
				var web = Web.GetWebByUrl(url, site);
				if (web == null) { throw new Exception("Failed to get SPWeb from url"); }

				// get item
				item = web.GetListItem(url);
				if (item == null) { throw new Exception("Failed to get SPListItem from url"); }
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("567807tuyGGfhgddfdfdtr", ex);
				item = null;
			}
			return item;
		}

		/// <summary>
		/// Get SPListItem by url passed
		/// </summary>
		/// <param name="url"></param>
		/// <returns></returns>
		public static SPListItem GetItemByUrl(string url)
		{
			return GetItemByUrl(url, Site.GetSite());
		}

		/// <summary>
		/// Delegate to format a single SPListItem
		/// (used for displaying many items)
		/// </summary>
		/// <param name="item"></param>
		/// <returns>HTML for a sinple SPListItem</returns>
		public delegate string ItemFormatted(SPListItem item);
		/// <summary>
		/// Default method for the ItemFormatted delegate
		/// Formats items with linked title
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public static string ItemFormattedDefault(SPListItem item)
		{
			string title = System.Web.HttpUtility.HtmlEncode(SPHelper.Field.GetStringValue(item, "Title"));
			string url = SPHelper.Item.GetUrl(item);
			return string.Format("<li><a href='{0}'>{1}</a></li>", url, title);
		}

		/// <summary>
		/// Delegate returns the key that the list of items will be groups by
		/// For example, to group by month, this delegate could return the month number as key
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public delegate string GroupByKey(SPListItem item);

		/// <summary>
		/// Delegate returns the display name of the group by key
		/// For example, to group by month, this delegate could return the full month display name
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public delegate string GroupByValue(SPListItem item);

		/// <summary>
		/// Delegate returns the keys and values that the list of items will be grouped by
		/// For example, to group by type, will return collection of all types item is under
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public delegate List<KeyValuePair<string, string>> GroupByKeyValues(SPListItem item);

		/// <summary>
		/// Delegate returns group heading, formatted
		/// For example, if grouping by month, might return month name in h2 tag, with anchor tag, and horizontal rule separator
		/// </summary>
		/// <param name="groupByKeyValue"></param>
		/// <returns>HTML for a group heading</returns>
		public delegate string GroupByHeaderFormatted(string groupByKey, string groupByValue, string listStartTag);
		/// <summary>
		/// Default method for GroupByHeadingFormatted delegate. 
		/// formats group value with anchor bookmark tag, h2 tags, then value
		/// </summary>
		/// <param name="groupByKeyValue"></param>
		/// <returns></returns>
		public static string GroupByHeaderFormattedDefault(string groupByKey, string groupByValue, string listStartTag)
		{
			string value = null;
			if (string.IsNullOrEmpty(groupByValue)) { groupByValue = groupByKey; }
			if (!string.IsNullOrEmpty(groupByKey)) { groupByKey = System.Web.HttpUtility.HtmlEncode(groupByKey); }
			if (!string.IsNullOrEmpty(groupByValue)) { groupByValue = System.Web.HttpUtility.HtmlEncode(groupByValue); }
			
			if (!string.IsNullOrEmpty(groupByKey)) { value += "<a name='" + groupByKey + "'></a>"; }
			if (!string.IsNullOrEmpty(groupByValue)) { value += "<h2>" + groupByValue + "</h2>"; }
			value += listStartTag;
			return value;
		}
		/// <summary>
		/// Delegate returns group footer, formatted
		/// useful for when you want to add a 'More Link' at bottom of the group of items
		/// </summary>
		/// <param name="groupByKeyValue"></param>
		/// <returns></returns>
		public delegate string GroupByFooterFormatted(string groupByKey, string groupByValue, string listEndTag);
		public static string GroupByFooterFormattedDefault(string groupByKey, string groupByValue, string listEndTag)
		{
			return listEndTag;
		}

		public static string DisplayItems(SPListItemCollection items, ItemFormatted itemFormatted)
		{
			// convert collection to generic
			var itemsList = ConvertSPListItemCollectionToGenericCollection(items);

			// call original
			var devNull = new List<KeyValuePair<string, string>>();
			return displayItems(itemsList, itemFormatted, delegate { return null; }, delegate { return null; },
				delegate { return null; }, false, false, 
				delegate { return null; }, null, 
				delegate { return null; }, null, 
				ref devNull);
		}

		public static string DisplayItems(SPListItemCollection items, ItemFormatted itemFormatted,
			string headerHtml, string footerHtml)
		{
			// convert collection to generic
			var itemsList = ConvertSPListItemCollectionToGenericCollection(items);

			// call original
			var devNull = new List<KeyValuePair<string, string>>();

			return displayItems(itemsList, itemFormatted, delegate { return null; }, delegate { return null; },
				delegate { return null; }, false, false, 
				delegate { return headerHtml; }, null, 
				delegate { return footerHtml; }, null, 
				ref devNull);
		}

		public static string DisplayItems(List<SPListItem> items, ItemFormatted itemFormatted,
			string headerHtml, string footerHtml)
		{
			// call original
			var devNull = new List<KeyValuePair<string, string>>();
			return displayItems(items, itemFormatted, delegate { return null; }, delegate { return null; },
				delegate { return null; }, false, false, 
				delegate { return headerHtml; }, null,
				delegate { return footerHtml; }, null, ref devNull);
		}

		public static string DisplayItems(SPListItemCollection items, ItemFormatted itemFormatted, 
			GroupByHeaderFormatted headerFormatted, GroupByFooterFormatted footerFormatted)
		{
			// convert collection to generic
			var itemsList = ConvertSPListItemCollectionToGenericCollection(items);

			// call original
			var devNull = new List<KeyValuePair<string, string>>();
			return displayItems(itemsList, itemFormatted, delegate { return null; }, delegate { return null; },
				delegate { return null; }, false, false, 
				headerFormatted, null, 
				footerFormatted, null, 
				ref devNull);
		}

		public static string DisplayItems(List<SPListItem> items, ItemFormatted itemFormatted,
			GroupByHeaderFormatted headerFormatted, GroupByFooterFormatted footerFormatted)
		{
			// call original
			var devNull = new List<KeyValuePair<string, string>>();
			return displayItems(items, itemFormatted, delegate { return null; }, delegate { return null; },
				delegate { return null; }, false, false, 
				headerFormatted, null, 
				footerFormatted, null,
				ref devNull);
		}

		 

		public static string DisplayItemsGrouped(SPListItemCollection items, ItemFormatted itemFormatted,
			GroupByKey groupByKey, GroupByValue groupByValue, bool isReOrderItemsByGroup,
			GroupByHeaderFormatted groupByHeaderFormatted, string listStartTag,
			GroupByFooterFormatted groupByFooterFormatted, string listEndTag, 
			ref List<KeyValuePair<string, string>> groupByKeyValuesUsed)
		{
			// convert collection to generic
			var itemsList = ConvertSPListItemCollectionToGenericCollection(items);

			// call original
			return displayItems(itemsList, itemFormatted, groupByKey, groupByValue, delegate { return null; },
				isReOrderItemsByGroup, false, 
				groupByHeaderFormatted, listStartTag, groupByFooterFormatted, listEndTag, 
				ref groupByKeyValuesUsed);
		}

		public static string DisplayItemsGrouped(SPListItemCollection items, ItemFormatted itemFormatted,
			GroupByKey groupByKey, GroupByValue groupByValue, bool isReOrderItemsByGroup,
			string headerHtml, string footerHtml,
			ref List<KeyValuePair<string, string>> groupByKeyValuesUsed)
		{
			// convert collection to generic
			var itemsList = ConvertSPListItemCollectionToGenericCollection(items);

			// call original
			return displayItems(itemsList, itemFormatted, groupByKey, groupByValue, delegate { return null; },
				isReOrderItemsByGroup, false, GroupByHeaderFormattedDefault, headerHtml,
				GroupByFooterFormattedDefault, footerHtml, ref groupByKeyValuesUsed);
		}

		public static string DisplayItemsGroupedByKeyValues(SPListItemCollection items, ItemFormatted itemFormatted,
			GroupByKeyValues groupByKeyValues, bool isReOrderItemsByGroup,
			GroupByHeaderFormatted groupByHeaderFormatted, string listStartTag,
			GroupByFooterFormatted groupByFooterFormatted, string listEndTag, 
			ref List<KeyValuePair<string, string>> groupByKeyValuesUsed)
		{
			// convert collection to generic
			var itemsList = ConvertSPListItemCollectionToGenericCollection(items);

			// call original
			return displayItems(itemsList, itemFormatted, delegate { return null; }, delegate { return null; },
				groupByKeyValues, isReOrderItemsByGroup, true, 
				groupByHeaderFormatted, listStartTag, 
				groupByFooterFormatted, listEndTag, 
				ref groupByKeyValuesUsed);
		}

		public static string DisplayItemsGroupedByKeyValues(SPListItemCollection items, ItemFormatted itemFormatted,
			GroupByKeyValues groupByKeyValues, bool isReOrderItemsByGroup,
			string headerHtml, string footerHtml,
			ref List<KeyValuePair<string, string>> groupByKeyValuesUsed)
		{
			// convert collection to generic
			var itemsList = ConvertSPListItemCollectionToGenericCollection(items);

			// call original
			return displayItems(itemsList, itemFormatted, delegate { return null; }, delegate { return null; },
				groupByKeyValues, isReOrderItemsByGroup, true,
				GroupByHeaderFormattedDefault, headerHtml,
				GroupByFooterFormattedDefault, footerHtml, 
				ref groupByKeyValuesUsed);
		}

		/// <summary>
		/// Display list of items, grouped by date
		/// By default, sorts by 'Month, day year'. To group by Month, pass "MMMM", to group by year, pass "yyyy"
		/// </summary>
		/// <param name="items"></param>
		/// <param name="itemFormatted">Delegate to format each item w/ HTML</param>
		/// <param name="dateTimeFieldInternalName">This is the field to get the date value from</param>
		/// <param name="groupByDateFormatString">Default is "MMMM d, yyyy"</param>
		/// <param name="unorderedListParameters">This can include class and inline styles that will be added to ul tag</param>
		/// <param name="groupByKeyValuesUsed">List of group by values that were found in the items passed</param>
		/// <returns></returns>
		public static string DisplayItemsGroupedByDate(SPListItemCollection items, ItemFormatted itemFormatted,
			string dateTimeFieldInternalName, string groupByDateFormatString,
			GroupByHeaderFormatted groupByHeaderFormatted, string listStartTag,
			GroupByFooterFormatted groupByFooterFormatted, string listEndTag, 
			ref List<KeyValuePair<string, string>> groupByKeyValuesUsed)
		{
			string o = null;
			try
			{
				// init
				if (string.IsNullOrEmpty(dateTimeFieldInternalName)) { dateTimeFieldInternalName = "Modified"; }
				if (string.IsNullOrEmpty(groupByDateFormatString)) { groupByDateFormatString = "MMMM d, yyyy"; }


				// get the group by field value. This is used to determine what group the current items should be in
				GroupByKey groupByKey = delegate(SPListItem item)
				{
					var dateTime = SPHelper.Field.GetDateTimeValue(item, dateTimeFieldInternalName);
					if (dateTime != null)
					{
						return dateTime.Value.ToString(groupByDateFormatString);
					}
					else { return null; }
				};

				// call orignal
				o = DisplayItemsGrouped(items, itemFormatted, groupByKey, delegate { return null; }, false, 
					groupByHeaderFormatted, listStartTag, 
					groupByFooterFormatted, listEndTag, 
					ref groupByKeyValuesUsed);
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("ertryuydsdstsdtr", ex);
				o = null;
			}
			return o;
		}

		/// <summary>
		/// Display list of items, grouped by date
		/// By default, sorts by 'Month, day year'. To group by Month, pass "MMMM", to group by year, pass "yyyy"
		/// </summary>
		/// <param name="items"></param>
		/// <param name="itemFormatted">Delegate to format each item w/ HTML</param>
		/// <param name="dateTimeFieldInternalName">This is the field to get the date value from</param>
		/// <param name="groupByDateFormatString">Default is "MMMM d, yyyy"</param>
		/// <param name="listStartTag">This can include class and inline styles that will be added to ul tag</param>
		/// <param name="listEndTag">This is the tag that ends a list</param>
		/// <param name="groupByKeyValuesUsed">List of group by values that were found in the items passed</param>
		/// <returns></returns>
		public static string DisplayItemsGroupedByDate(SPListItemCollection items, ItemFormatted itemFormatted,
			string dateTimeFieldInternalName, string groupByDateFormatString, string listStartTag, string listEndTag, 
			ref List<KeyValuePair<string, string>> groupByKeyValuesUsed)
		{
			// init
			if (string.IsNullOrEmpty(dateTimeFieldInternalName)) { dateTimeFieldInternalName = "Modified"; }
			if (string.IsNullOrEmpty(groupByDateFormatString)) { groupByDateFormatString = "MMMM d, yyyy"; }

			GroupByKey groupByKey = delegate(SPListItem item)
			{
				var dateTime = SPHelper.Field.GetDateTimeValue(item, dateTimeFieldInternalName);
				if (dateTime != null)
				{
					return dateTime.Value.ToString(groupByDateFormatString);
				}
				else { return null; }
			};

			// call original
			return displayItems(ConvertSPListItemCollectionToGenericCollection(items), itemFormatted, groupByKey, 
				delegate { return null; }, delegate { return null; }, false, false,
				GroupByHeaderFormattedDefault, listStartTag,
				GroupByFooterFormattedDefault, listEndTag, 
				ref groupByKeyValuesUsed);
		}
	
		/// <summary>
		/// Display a list of SPListItems
		/// </summary>
		/// <param name="items"></param>
		/// <param name="itemFormatted">delegate to return each individual item in html format</param>
		/// <param name="groupByKeyValue">delegate to return the key value pair to group by</param>
		/// <param name="groupByHeaderFormatted">delegate to return the group header formatted</param>
		/// <param name="groupByFooterFormatted">delegate to return the group footer formatted, useful for 'More Link'</param>
		/// <param name="unorderedListParameters">This can include class and inline styles that will be added to ul tag</param>
		/// <param name="groupByKeyValuesUsed">List of group by values that were found in the items passed</param>
		/// <returns></returns>
		private static string displayItems(List<SPListItem> items, ItemFormatted itemFormatted,
			GroupByKey groupByKey, GroupByValue groupByValue, GroupByKeyValues groupByKeyValues,
			bool isReOrderItemsByGroup, bool isItemsUnderMoreThanOneGroup,
			GroupByHeaderFormatted groupByHeaderFormatted, string listStartTag,
			GroupByFooterFormatted groupByFooterFormatted, string listEndTag, 
			ref List<KeyValuePair<string, string>> groupByKeyValuesUsed)
		{
			string o = null;
			try
			{
				// init
				if (items == null) { throw new Exception("SPListItems collection passed is null"); }
				if (groupByKeyValuesUsed == null) { groupByKeyValuesUsed = new List<KeyValuePair<string, string>>(); }


				string previousGroupKey = null;
				string previousGroupValue = null;
				string currentGroupKey = null;
				string currentGroupValue = null;
				bool isFirstItem = true;
				bool isFooterNeeded = false;

				// reorder for grouping?
				var groupByKeyValuesUsedAfterReorder = new Dictionary<int, KeyValuePair<string, string>>();
				if (items != null && items.Count > 0 && isReOrderItemsByGroup)
				{
					var devNull = new List<string>();
					items = reOrderItemsByGroup(items, isItemsUnderMoreThanOneGroup, 
						groupByKey, groupByValue, groupByKeyValues, ref groupByKeyValuesUsedAfterReorder);
				}

				foreach (SPListItem item in items)
				{
					///////////////////////////////////////////////////////////
					// GROUP
					
					// get current item's group key and display name
					currentGroupKey = null;
					currentGroupValue = null;
					if (isReOrderItemsByGroup)
					{
						if (groupByKeyValuesUsedAfterReorder.ContainsKey(item.ID))
						{
							currentGroupKey = groupByKeyValuesUsedAfterReorder[item.ID].Key;
							currentGroupValue = groupByKeyValuesUsedAfterReorder[item.ID].Value;
						}
					}
					else
					{
						currentGroupKey = groupByKey(item);
						currentGroupValue = groupByValue(item);
					}

					// new group
					if (currentGroupKey != previousGroupKey || isFirstItem)
					{
						// end previous group if not first element
						if (!isFirstItem) 
						{ 
							o += groupByFooterFormatted(previousGroupKey, previousGroupValue, listEndTag);
						}

						// add new group heading
						o += groupByHeaderFormatted(currentGroupKey, currentGroupValue, listStartTag);
						isFooterNeeded = true;

						// add to list of groups used
						if (!string.IsNullOrEmpty(currentGroupKey))
						{
							groupByKeyValuesUsed.Add(new KeyValuePair<string, string>(currentGroupKey, currentGroupValue));
						}
						previousGroupKey = currentGroupKey;
						previousGroupValue = currentGroupValue;
					}

					///////////////////////////////////////////////////////////
					// ITEM
					o += itemFormatted(item);

					if (isFirstItem) { isFirstItem = false; }
				} // foreach SPListItem

				// end ul tag
				if (isFooterNeeded) 
				{ 
					o += groupByFooterFormatted(currentGroupKey, currentGroupValue, listEndTag);
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("685urtertresersdtrseesres6", ex);
				o = null;
			}
			return o;
		}


		

		

		/// <summary>
		/// This key is in the SPListItem property bag when doing a custom grouping of items
		/// </summary>
		public static string CustomGroupByPropertyKey = "___[groupKey]___";

		/// <summary>
		/// Re-order SPListItemCollection, grouping items and duplicating them if under more than one group
		/// </summary>
		/// <param name="itemsToOrder"></param>
		/// <param name="valuesToGroupBy">Delegate that returns a list of groups item falls under</param>
		/// <param name="groupValuesUsed">returns the groups found in passed list (except empty group)</param>
		/// <returns></returns>
		private static List<SPListItem> reOrderItemsByGroup(List<SPListItem> itemsToOrder, bool isItemUnderMoreThanOneGroup, 
			GroupByKey groupByKey, GroupByValue groupByValue, GroupByKeyValues groupByKeyValues, 
			ref Dictionary<int,KeyValuePair<string, string>> groupKeyValuesUsed)
		{
			var itemsOrdered = new List<SPListItem>();
			try
			{
				// init
				if (groupKeyValuesUsed == null) { groupKeyValuesUsed = new Dictionary<int,KeyValuePair<string,string>>(); }
				var emptyGroupKeyValue = new KeyValuePair<string, string>("___[EMPTY]___", null);


				if (itemsToOrder != null && itemsToOrder.Count > 0)
				{
					// build dictionary of group by values
					var groups = new SortedDictionary<string, List<SPListItem>>();
					foreach (SPListItem item in itemsToOrder)
					{
						// get groups item is under
						var groupsItemUnder = new List<KeyValuePair<string, string>>();
						if (isItemUnderMoreThanOneGroup)
						{
							groupsItemUnder = groupByKeyValues(item);
							if (groupsItemUnder == null || groupsItemUnder.Count == 0)
							{
								groupsItemUnder = new List<KeyValuePair<string, string>>() { emptyGroupKeyValue };
							}
						}
						else
						{
							var key = groupByKey(item);
							var value = groupByValue(item);
							if (string.IsNullOrEmpty(value)) { value = key; }
							groupsItemUnder.Add(new KeyValuePair<string,string>(key, value));
						}
						
						foreach (var groupKeyValue in groupsItemUnder)
						{
							if (!groups.ContainsKey(groupKeyValue.Key))
							{
								// add group, it is new
								groups.Add(groupKeyValue.Key, new List<SPListItem>() { item });
							}
							else
							{
								// add to existing group
								groups[groupKeyValue.Key].Add(item);
							}

							// add this key/value that item is under to lookup
							if (groupKeyValue.Key != emptyGroupKeyValue.Key)
							{
								groupKeyValuesUsed.Add(item.ID, groupKeyValue);
							}
						}
					}

					// merge into one list
					foreach (var group in groups)
					{
						foreach (SPListItem item in group.Value) { itemsOrdered.Add(item); }
					}

				} // SPListItemCollection passed not null and has items
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("456i7r5e4yteetsfdsdsfdfre", ex);
				itemsOrdered = null;
			}
			return itemsOrdered;
		}

		

	}
}
